/*
 * Created on 16. aug.. 2006
 * Created by Tomas Ekeli
 */
package gui;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.AbstractTableModel;

import people.Group;
import people.Person;
import placing.Mapper;
import placing.ReservationConflictException;
import placing.Room;
import placing.RoomException;

/**
 * 
 * Presents the GUI
 * 
 * @author Tomas
 *
 */
public class Presenter{
	private final String TITLE 					= "Roomy";
	private final String LISTS_BORDER_TITLE 	= "Overview";
	private final String DETAILS_BORDER_TITLE 	= "Details";	
	private final String ROOM_DETAIL		= "Room";
	private final String PERSON_DETAIL 		= "Person";
	private final String GROUP_DETAIL		= "Group";
	private final String PERSON_LIST 		= "Persons";
	private final String ROOMS_LIST 		= "Rooms";
	private final String GROUPS_LIST		= "Groups";
	private final String ROOM_LIST_HINT 	= "Display details about the room";
	private final String PERSON_DETAIL_HINT = "Display details about the person";
	private final String GROUP_DETAIL_HINT	= "Display details about the group";
	private final String PERSON_LIST_HINT 	= "Display overview of persons";
	private final String ROOMS_LIST_HINT 	= "Display overview of rooms";
	private final String GROUPS_LIST_HINT	= "Display overview of groups";
	
	//Buttons
	JButton def, assignButton, loadButton, saveButton;
	
	//Models that link to the data
	private RoomTreeModel roomModel;
	private AbstractTableModel personsModel;
	
	//What the detail-panels should show
	private List<Person> currentPersons;
	private List<Group> currentGroups;
	private List<Room> currentRooms;
	
	//Frame
	JFrame frame;
	
	//Detailed views
	PersonsDetailPanel personView;
	JPanel groupView;
	JPanel roomView;
	
	//Default file
	String filename = "default.map";
	
	//The manager
	private Mapper mapper;
	
	//The size of the GUI
	private int width = 800;
	private int height = 600;
		
	public Presenter() {
		mapper = new Mapper();
		
		//for testing we add some members
		addTestData();
	}
	
	protected Mapper getCurrentMapper() {
		return mapper;
	}
	
	protected List<Room> getCurrentRooms() {
		return currentRooms;
	}

	protected List<Person> getCurrentPersons() {
		return currentPersons;
	}

	protected List<Group> getCurrentGroups() {
		return currentGroups;
	}

	/**
	 * Create the components that can be displayed
	 *
	 */
	private JComponent createAllComponents() {
		
		//This section presents persons
		JPanel listsPanel = createListsPanel();
		
		//This section presents rooms
		JPanel detailsPanel = createDetailsPanel();
		
		//This section displays administrative tools
		JPanel toolsPanel = createToolsPanel();
		
		//Make the contentPanel		
		JPanel panel = new JPanel(new GridLayout(1, 0));

		//Add the components
		panel.add(toolsPanel);
		panel.add(listsPanel);
		panel.add(detailsPanel);
		
		
		//Add a border around the components
		panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 2, 5));
		
		
		return panel;
		
	}
	
	/*
	 * Create the panel with the status-info and the buttons
	 */
	private JPanel createToolsPanel() {
		JPanel administrativeToolsPanel = new JPanel(new GridLayout(0, 1));
		
		//create the info pane
		JPanel infoPanel = createTestTextPanel("Info panel");
		infoPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 2, 5));
		infoPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		
		//create the buttons pane
		JPanel buttonsPanel = createButtonsPanel();
		
		administrativeToolsPanel.add(infoPanel);
		administrativeToolsPanel.add(buttonsPanel);
		
		return administrativeToolsPanel;
	}
	
	
	/*
	 * Create the panel with buttons
	 */
	private JPanel createButtonsPanel() {
		JPanel panel = new JPanel();
		ButtonListener listener = new ButtonListener();
		
		assignButton = new JButton("Assign persons");
		saveButton = new JButton("Save to disk");
		loadButton = new JButton("Load from disk");
		
		assignButton.addActionListener(listener);
		saveButton.addActionListener(listener);
		loadButton.addActionListener(listener);
		
		panel.add(assignButton);
		panel.add(saveButton);
		panel.add(loadButton);
		
		def = new JButton("Default button");
		def.setDefaultCapable(true);
		panel.add(def);
		
		panel.setBorder(BorderFactory.createEmptyBorder(30, 30, 10, 30));
		return panel;
	}


	/*
	 * Create the panel with the details
	 */
	private JPanel createDetailsPanel() {
		//create the presentation of data
		JTabbedPane roomsTabbedPane = createDetailsTabbedPanel();
		
		//create the border
		Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
		Border titledBorder = BorderFactory.createTitledBorder(etchedBorder, DETAILS_BORDER_TITLE);
		
		//arrange it all
		JPanel detailsPanel = new JPanel();
		detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS));
		detailsPanel.add(roomsTabbedPane);
		detailsPanel.setBorder(titledBorder);
		return detailsPanel;
	}


	/*
	 * Create the tabbed pane where the detailed info about the Room, Group or
	 * Person is shown
	 */
	private JTabbedPane createDetailsTabbedPanel() {
		JTabbedPane tabbedPanel = new JTabbedPane();
		
		//The panel with the Room detail
		//TODO Room detail panel is just a placeholder
		roomView = createTestTextPanel("Room detail view"); 
		
		//The panel with the Person detail
		personView = createPersonsDetailPanel(); 
		
		//The panel with the Group detail
		//TODO Group detail panel is just a placeholder
		groupView = createTestTextPanel("Group detail view");
		
		//Add the list and detail views to the Person tabbed pane
		tabbedPanel.addTab(ROOM_DETAIL, null, roomView, ROOM_LIST_HINT);
		tabbedPanel.addTab(PERSON_DETAIL, null, personView, PERSON_DETAIL_HINT);
		tabbedPanel.addTab(GROUP_DETAIL, null, groupView, GROUP_DETAIL_HINT);
		tabbedPanel.setPreferredSize(new Dimension(300, 400));
		
		return tabbedPanel;
	}

	/**
	 * Creates the panel that displays the details about a single Person
	 * @return
	 */
	private PersonsDetailPanel createPersonsDetailPanel() {
		PersonsDetailPanel panel = new PersonsDetailPanel(this);
		
		return panel;
	}

	/*
	 * Create the Panel with overviews of the rooms, persons and groups
	 */
	private JPanel createListsPanel() {
		//create the presentation of data
		JTabbedPane personTabbedPane = createListsTabbedPanel();
		
		//create the border
		Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
		Border titledBorder = BorderFactory.createTitledBorder(etchedBorder, LISTS_BORDER_TITLE);
		
		//arrange it all
		JPanel listsPanel = new JPanel();
		listsPanel.setLayout(new BoxLayout(listsPanel, BoxLayout.Y_AXIS));
		listsPanel.add(personTabbedPane);
		listsPanel.setBorder(titledBorder);
		return listsPanel;
	}


	/*
	 * Create the tabbed pane that presents the overviews of Persons, Rooms and 
	 * Groups 
	 */
	private JTabbedPane createListsTabbedPanel() {
		JTabbedPane tabbedPanel = new JTabbedPane();
		
		//The panel with the Room list
		//TODO Room List panel isn't done yet
		RoomOverviewPanel roomsListPanel = new RoomOverviewPanel(this);
		roomModel = (RoomTreeModel) roomsListPanel.getTree().getModel();
		
		roomsListPanel.getTree().addTreeSelectionListener(new RoomTreeListener(roomModel));
		
		
		//The panel with the Groups list
		//TODO Group list view panel is just a placeholder
		JPanel groupsListPanel = createTestTextPanel("Group list view"); 
		
		//The panel with the Persons list
		PersonsOverviewPanel personsListPanel = new PersonsOverviewPanel(this);
		personsModel = (AbstractTableModel) personsListPanel.getTable().getModel();
		((PersonsOverviewPanel)personsListPanel).addSelectionListener(new PersonSelectionListener());
		
		//Add the list and detail views to the Person tabbed pane
		tabbedPanel.addTab(ROOMS_LIST, null, roomsListPanel, ROOMS_LIST_HINT);
		tabbedPanel.addTab(PERSON_LIST, null, personsListPanel, PERSON_LIST_HINT);
		tabbedPanel.addTab(GROUPS_LIST, null, groupsListPanel, GROUPS_LIST_HINT);
		tabbedPanel.setPreferredSize(new Dimension(300, 400));
		
		return tabbedPanel;
	}


	/**
	 * Initialise the gui
	 *
	 */
	private void createAndShowGUI() {		
		//make a frame
		frame = new JFrame(TITLE);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setPreferredSize(new Dimension(width, height));
		
		//add the components
		frame.add(createAllComponents());
		
		//Set the default button
		frame.getRootPane().setDefaultButton(def);
		
		//show the GUI
		frame.pack();
		frame.setLocationRelativeTo(null); //centre on screen
		frame.setVisible(true);
		
		System.out.println("Members: " + mapper.getNumPersons());
	}
	
	/*
	 * For testing
	 */
	private JPanel createTestTextPanel(String text) {
		JPanel panel = new JPanel();
		JLabel label = new JLabel(text);
		label.setHorizontalAlignment(SwingConstants.CENTER);
		panel.setLayout(new GridLayout(1,1));
		panel.add(label);
		
		return panel;
	}

	/*
	 * Let the models know that something has changed in the datastructure
	 * they are modelling
	 */
	protected void updateModels() {
		roomModel.valueForPathChanged(null, null);
		personsModel.fireTableDataChanged();
		updateDetailViews();
	}

	/**
	 * The currently selected Person, Group or Room may have changed. Update
	 * the detail-views to reflect this.
	 *
	 */
	private void updateDetailViews() {
		if (currentPersons.size() != 0) {
			personView.present(currentPersons.get(0));
		}
	}

	/*
	 * For testing
	 */
	private void addTestData() {
		int nBlackAdder 	= 30;
		int nFryAndLaurie 	= 25;
		int nFunnyMen 		= 18;
		int nQueer 			= 100;
		
		Person rowan 	= new Person(1, "Rowan",  "Atkinson");
		Person hugh 	= new Person(2, "Hugh", "Laurie");
		Person stephen	= new Person(3, "Stephen", "Fry");
		Person tony 	= new Person(4, "Tony", "Robinson");
		Person tim 		= new Person(5, "Tim", "McInnerny");
		Person tomas	= new Person(6, "Tomas", "Ekeli");
		
		Group blackAdder = new Group("Black Adder", nBlackAdder);
		Group fryAndLaurie = new Group("A Bit of Fry And Laurie", nFryAndLaurie);
		Group funnyMen = new Group("Funny Men", nFunnyMen);
		Group queer = new Group("Queer and Here", nQueer);
		
		blackAdder.setScore(nBlackAdder);
		fryAndLaurie.setScore(nFryAndLaurie);
		funnyMen.setScore(nFunnyMen);
		queer.setScore(nQueer);
		
		
		try {
			blackAdder.addPerson(rowan);
			blackAdder.addPerson(hugh);
			blackAdder.addPerson(stephen);
			blackAdder.addPerson(tony);
			blackAdder.addPerson(tim);
			
			funnyMen.addPerson(rowan);
			funnyMen.addPerson(hugh);
			funnyMen.addPerson(stephen);
			
			fryAndLaurie.addPerson(hugh);
			fryAndLaurie.addPerson(stephen);
			
			queer.addPerson(stephen);
			
			mapper.addGroup(blackAdder);
			mapper.addGroup(funnyMen);
			mapper.addGroup(fryAndLaurie);
			mapper.addGroup(queer);
			
			mapper.addPerson(tomas);
			
			mapper.addRoom(3);
			mapper.addRoom(5);
			mapper.addRoom();
			
			mapper.reserveRoom(mapper.getRooms().get(0), blackAdder);
		}
		catch (RoomException e) {
			System.err.println("Coudln't add room\n" + e);
		}
	}


	/*
	 * Sets up the GUI and displays it on-screen
	 * Now thread-safe.
	 */
	public static void main(String[] args) {
		
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						Presenter p = new Presenter();
						p.createAndShowGUI();
					}
				});
	}


	/**
	 * 
	 * Listens for presses to the buttons, and executes the actions corresponding
	 * to them.
	 * 
	 * @author Tomas
	 *
	 */
	public class ButtonListener implements ActionListener {
	
		public void actionPerformed(ActionEvent e) {
			if (assignButton == e.getSource()) {
				try {
					mapper.distributePersonsToPlaces();
					updateModels();
				} catch (ReservationConflictException e1) {
					System.err.println("Error when assigning to places:\n" + e);
				}
			} else {
				try {
					if (loadButton == e.getSource()) {
						//load from file
						FileInputStream fis = new FileInputStream(filename);
						ObjectInputStream in = new ObjectInputStream(fis);
						mapper = (Mapper)in.readObject();
						updateModels();
					} else {
						if (saveButton == e.getSource()) {
							//save to file
							FileOutputStream fos = new FileOutputStream(filename);
							ObjectOutputStream out = new ObjectOutputStream(fos);
							out.writeObject(mapper);
							out.flush();
							out.close();
							
						} else {
							System.err.println("unknown source of event:\n" + e);
						}
						
					}
				}catch (FileNotFoundException exc) {
				System.err.println("Could not open file " + filename);
				} catch (IOException exc) {
					System.err.println("I/O exception accessing file " + filename);
					exc.printStackTrace();
				} catch (ClassNotFoundException exc) {
					System.err.println("File does not contain a mapper");
				}
			} 
	
		}
	
	}
	
	/**
	 * 
	 * Listens for selections on the tree displaying the Rooms. Uses this
	 * to update the detail view of the room.
	 * 
	 * @author Tomas Ekeli
	 *
	 */
	public class RoomTreeListener implements TreeSelectionListener {
		private RoomTreeModel model;
		
		
		public RoomTreeListener(RoomTreeModel m) {
			model = m;
		}


		public void valueChanged(TreeSelectionEvent e) {
			if (e.getNewLeadSelectionPath() == null) {
				return;
			}
			//TODO update the detailed views based on selection
			System.out.println("selection in tree");
			
		}

		
	
		
	}
	
	/**
	 * 
	 * Listens for selections on the overview of Persons
	 * 
	 * @author Tomas
	 *
	 */
	public class PersonSelectionListener {
		
		public void selectionChanged(List<Person> selected) {
			System.out.println("Selected:");
			for (Person p : selected) {
				System.out.println(p.getName());
			}
			currentPersons = selected;
			updateDetailViews();
		}
	}

}
	
